home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2004 April / Gamestar_61_2004-04_dvdb.iso / DVDStar / Editace / hltp.exe / {app} / Applications / QuArK / plugins / mapcamerapos.py < prev    next >
Text File  |  2004-01-05  |  14KB  |  486 lines

  1. # QuArK  -  Quake Army Knife
  2. #
  3. # Copyright (C) 2001 The Quark Community
  4. # THIS FILE IS PROTECTED BY THE GNU GENERAL PUBLIC LICENCE
  5. # FOUND IN FILE "COPYING.TXT"
  6. #
  7. #$Header: /cvsroot/quark/runtime/plugins/mapcamerapos.py,v 1.10 2003/12/18 21:51:46 peter-b Exp $
  8.  
  9. Info = {
  10.    "plug-in":       "Camera Position Duplicator",
  11.    "desc":          "storeable camera positions",
  12.    "date":          "13 June 2001",
  13.    "author":        "tiglari",
  14.    "author e-mail": "tiglari@planetquake.com",
  15.    "quark":         "Quark 6.3" }
  16.  
  17. #
  18. #  Lots of design suggestions by quantum_red.
  19. #
  20.  
  21. import quarkx
  22. #
  23. # The style of import statement here makes it unnecessary tp
  24. #  put 'quarkpy' into references to things in these modules.
  25. #  This would make it easier to shift this material directly
  26. #  into say the 'maphandles' module
  27. #
  28. from quarkpy import mapentities
  29. from quarkpy import mapduplicator
  30. from quarkpy import qhandles
  31. from quarkpy import qutils
  32. from quarkpy import mapsearch
  33. from quarkpy import dlgclasses
  34. from quarkpy import qmacro
  35. from quarkpy import mapselection
  36. from quarkpy import mapmenus
  37.  
  38. import mapmadsel
  39.  
  40. #
  41. # and because of this, things from maphhandles can be used
  42. #  w/o qualification
  43. #
  44. from quarkpy.maphandles import *
  45.         
  46.  
  47. #
  48. # Tries to find the last 3D view clicked on
  49. #
  50. def get3DView(editor,makeone=0):
  51.     #
  52.     # A bit of showoff coding, see the onlne Python tutorial,
  53.     #   functional programming techiques
  54.     #
  55.     views = filter(lambda v:v.info["type"]=="3D",editor.layout.views)
  56.     if len(views)==0:
  57.         if makeone:
  58.             editor.layout.new3Dwindow(None)
  59.             views = filter(lambda v:v.info["type"]=="3D",editor.layout.views)
  60.             return views[0]
  61.         else:
  62.             quarkx.msgbox("Open a 3D view",2,4)
  63.             return
  64.     elif len(views)==1:
  65.         return views[0]
  66.     for view in views:
  67.         #
  68.         # put it in an exception-catching block for in case
  69.         #   editor doesn't have a last3DView
  70.         #
  71.         try:
  72.             if view is editor.last3DView:
  73.                 return view
  74.         except (AttributeError):
  75.             pass
  76.     #
  77.     # If intelligent selection fails, take the first.
  78.     #
  79.     return views[0]
  80.  
  81. #
  82. # We're going to trigger these actions both by menu
  83. #  items and buttons in a dialog, so we define them
  84. #  independently of the UI elements that call them.
  85. #
  86. def setView(o,editor):
  87.     view = get3DView(editor,makeone=1)
  88.     if view is None:
  89.         return
  90.     view.cameraposition = o.origin, o["yaw"][0], o["pitch"][0]          
  91.     editor.invalidateviews()
  92.     editor.currentcampos=o
  93.  
  94. def storeView(o,editor):
  95.     view = get3DView(editor)
  96.     if view is None:
  97.         return
  98.     pos, yaw, pitch = view.cameraposition
  99.     undo = quarkx.action()
  100.     undo.setspec(o,"origin",str(pos))
  101.     #
  102.     # note setting values as 1-element tuples (Python docco)
  103.     #
  104.     undo.setspec(o,"yaw",(yaw,))
  105.     undo.setspec(o,"pitch",(pitch,))
  106.     editor.ok(undo,"store camera position")
  107.     editor.currentcampos=o
  108. #
  109. # The menu redefinition trick, as discussed in the plugin tutorial
  110. #  in the infobase.  'o' is the duplicator object
  111. #
  112. def camposmenu(o, editor, oldmenu=mapentities.DuplicatorType.menu.im_func):
  113.     "camera position entity menu"
  114.  
  115.     menu = oldmenu(o, editor)
  116.     if o["macro"] !="cameraposition":
  117.         return menu
  118.  
  119.     def setViewClick(m,o=o,editor=editor):
  120.         setView(o,editor)
  121.  
  122.     def storeViewClick(m,o=o,editor=editor):
  123.         storeView(o,editor)
  124.  
  125.     storeitem=qmenu.item("Store View",storeViewClick,"|Store 3D view position in this position item.")
  126.     getitem=qmenu.item("Set View", setViewClick,"|Set 3D view position from this position item.\n\nThen use PgUp/Down with 'C' depressed to cycle prev/next camera positions in the same group as this one.\n\nThis won't work until a view has been set or stored in the group with the menu item")
  127.     return [getitem, storeitem]
  128.   
  129. mapentities.DuplicatorType.menu = camposmenu
  130.  
  131. #
  132. # Icons in the treeview represent map objects directly,
  133. #   while icons in the map views represent their handles.
  134. #   the code here uses the object's menu as the handle's.
  135. #
  136. class CamPosHandle(qhandles.IconHandle):
  137.  
  138.     def __init__(self, origin, centerof):
  139.         qhandles.IconHandle.__init__(self, origin, centerof)
  140.  
  141.     def menu(self, editor, view):
  142.         return camposmenu(self.centerof, editor)
  143.  
  144. #
  145. # The creation of an extremely simple duplicator ...
  146. # Define a class and register it by updating the DupCodes.
  147. #
  148. class CamPosDuplicator(mapduplicator.StandardDuplicator):
  149.  
  150.     def handles(self, editor, view):
  151.         org = self.dup.origin
  152.         if org is None:
  153.             org = quarkx.vect(self.dup["origin"])
  154.         hndl = CamPosHandle(org, self.dup)
  155.         return [hndl]
  156.  
  157. mapduplicator.DupCodes.update({
  158.   "cameraposition":  CamPosDuplicator,
  159. })
  160.  
  161. #
  162. # See the dialog box section in the advanced customization
  163. #  section of the infobase.  SimpleCancelDlgBox is
  164. #  defined in quarkpy.qeditor.
  165. #
  166. class NameDialog(SimpleCancelDlgBox):
  167.     "A simple dialog box to enter a name."
  168.  
  169.     endcolor = AQUA
  170.     size = (330,135)
  171.     dfsep = 0.45
  172.     dlgdef = """
  173.       {
  174.         Style = "9"
  175.         Caption = "Enter name"
  176.         sep: = {Typ="S" Txt=" "}    // some space
  177.         name: = {
  178.           Txt=" Enter the name :"
  179.           Typ="E"
  180.         }
  181.         local: = {Typ="X" Hint="Put camera in currently selected group, if possible" $0D " (sister to selected non-group)"}
  182.         sep: = {Typ="S" Txt=" "}    // some space
  183.         sep: = {Typ="S" Txt=""}    // a separator line
  184.         cancel:py = {Txt="" }
  185.       }
  186.     """
  187.  
  188.     def __init__(self, form, action, initialvalue=None):
  189.         src = quarkx.newobj(":")
  190.         if initialvalue is not None:
  191.            src["name"]=initialvalue
  192.         self.initialvalue=initialvalue
  193.         self.action=action
  194.         SimpleCancelDlgBox.__init__(self, form, src)
  195.  
  196.     #
  197.     # This is executed when the data changes, close when a new
  198.     #   name is provided
  199.     #
  200.     def datachange(self, df):
  201.         if self.src["name"]!=self.initialvalue:
  202.             self.close()
  203.  
  204.  
  205.     #
  206.     # This is executed when the OK button is pressed
  207.     #   FIXME: 'local' code doesn't work right, dialog
  208.     #   would need some redesign
  209.     #
  210.     def ok(self):
  211.         name = self.src["name"]
  212.         if name is None:
  213.             name="Camera Position"
  214.         self.name=name
  215.         self.local = self.src["local"]
  216.         self.action(self)
  217.  
  218. #
  219. # This is called by two interface items, so pulled
  220. #  out of both of them
  221. #
  222. def addPosition(view3D, editor):
  223.         #
  224.         # Dialogs run 'asynchronously', which means
  225.         #  that the after the creation of the dialog just
  226.         #  runs without waiting for a value to be entered
  227.         #  into the dialog.  So if you don't want something
  228.         #  to happen until then, you need to code it in a
  229.         #  function that gets passed to the dialog as
  230.         #  a parameter, which is what this is.
  231.         #
  232.         def action(self, view3D=view3D,editor=editor):
  233.             #
  234.             # NB: elsewhere in the code, 'yaw' tends to
  235.             # be misnamed as 'roll'
  236.             #
  237.             #  pitch = up/down angle (relative to x axis)
  238.             #  yaw = left/right angle (relative to x axis)
  239.             #  roll = turn around long axis (relative to y)
  240.             #
  241.             pos, yaw, pitch = view3D.cameraposition
  242.             camdup = quarkx.newobj(self.name+":d")
  243.             camdup["macro"] = "cameraposition"
  244.             pozzies=None
  245.             if self.src["local"]:
  246.                 sel = editor.layout.explorer.uniquesel
  247.                 if sel is not None:
  248.                     if sel.type==":g":
  249.                         pozzies = sel
  250.                     else:
  251.                        pozzies=sel.treeparent # returns None if parent outside of tree                   
  252.             if pozzies is None:    
  253.                 pozzies = editor.Root.findname("Camera Positions:g")
  254.             undo=quarkx.action()
  255.             if pozzies is None:
  256.                 pozzies = quarkx.newobj("Camera Positions:g")
  257.                 undo.put(editor.Root,pozzies)
  258.             undo.put(pozzies, camdup)
  259.             undo.setspec(camdup,"origin",str(pos))
  260.             undo.setspec(camdup,"yaw",(yaw,))
  261.             undo.setspec(camdup,"pitch",(pitch,))
  262.             editor.ok(undo,'add camera position')
  263.         #
  264.         # Now execute the dialog
  265.         #
  266.         NameDialog(quarkx.clickform,action,"Camera Position")
  267.  
  268.  
  269.  
  270. #
  271. # And more menu redefinition, this time for the
  272. #  EyePositionMap handle defined in maphandles.py.
  273. #
  274. def newEyePosMenu(self, editor, view):
  275.     
  276.     def addClick(m,self=self,editor=editor):
  277.         addPosition(self.view3D,editor)
  278.         
  279.     item = qmenu.item('Add position',addClick)
  280.     return [item]
  281.  
  282. EyePositionMap.menu = newEyePosMenu
  283.  
  284. def backmenu(editor, view=None, origin=None, oldbackmenu = mapmenus.BackgroundMenu):
  285.   menu = oldbackmenu(editor, view, origin)
  286.  
  287.   def addClick(m,view=view,editor=editor):
  288.       addPosition(view,editor)
  289.       
  290.   if view is not None and view.info["type"]=="3D":
  291.       menu.append(qmenu.item("Add Camera Position",addClick))
  292.   return menu
  293.  
  294. mapmenus.BackgroundMenu = backmenu
  295.  
  296.  
  297.  
  298. #
  299. # A Live Edit dialog.  Closely modelled on the Microbrush
  300. #  H/K dialog, so look at that for enlightenment
  301. #
  302. class FindCameraPosDlg(dlgclasses.LiveEditDlg):
  303.     #
  304.     # dialog layout
  305.     #
  306.  
  307.     endcolor = AQUA
  308.     size = (220,160)
  309.     dfsep = 0.35
  310.     dlgflags = qutils.FWF_KEEPFOCUS 
  311.     
  312.     dlgdef = """
  313.         {
  314.         Style = "9"
  315.         Caption = "Camera position finder"
  316.  
  317.         cameras: = {
  318.           Typ = "C"
  319.           Txt = "Positions:"
  320.           Items = "%s"
  321.           Values = "%s"
  322.           Hint = "These are the camera positions.  Pick one," $0D " then push buttons on row below for action."
  323.         }
  324.  
  325.           
  326.         sep: = { Typ="S" Txt=""}
  327.  
  328.         buttons: = {
  329.         Typ = "PM"
  330.         Num = "3"
  331.         Macro = "camerapos"
  332.         Caps = "TVS"
  333.         Txt = "Actions:"
  334.         Hint1 = "Select the chosen one in the treeview"
  335.         Hint2 = "Set the view to the chosen one"
  336.         Hint3 = "Store the view in the chosen one"
  337.         }
  338.  
  339.         num: = {
  340.           Typ = "EF1"
  341.           Txt = "# found"
  342.         }
  343.  
  344.         sep: = { Typ="S" Txt=""}
  345.  
  346.         exit:py = {Txt="" }
  347.     }
  348.     """
  349.  
  350.     def select(self):
  351.         index = eval(self.chosen)
  352.         #
  353.         # FIXME: dumb hack, revise mapmadsel
  354.         #
  355.         m = qmenu.item("",None)
  356.         m.object=self.pack.cameras[index]
  357.         mapmadsel.SelectMe(m)
  358.  
  359.     def setview(self):
  360.         index = eval(self.chosen)
  361.         editor=mapeditor()
  362.         if editor is None:
  363.             quarkx.msgbox('oops no editor',2,4)
  364.         setView(self.pack.cameras[index],editor)
  365.                 
  366.     def storeview(self):
  367.         index = eval(self.chosen)
  368.         editor=mapeditor()
  369.         if editor is None:
  370.             quarkx.msgbox('oops no editor',2,4)
  371.         storeView(self.pack.cameras[index],editor)
  372.                 
  373.     
  374. #
  375. # Define the zapview macro here, put the definition into
  376. #  quarkpy.qmacro, which is where macros called from delphi
  377. #  live.
  378. #
  379. def macro_camerapos(self, index=0):
  380.     editor = mapeditor()
  381.     if editor is None: return
  382.     if index==1:
  383.         editor.cameraposdlg.select()
  384.     elif index==2:
  385.         editor.cameraposdlg.setview()
  386.     elif index==3:
  387.         editor.cameraposdlg.storeview()
  388.         
  389. qmacro.MACRO_camerapos = macro_camerapos
  390.  
  391. def findClick(m):
  392.     editor=mapeditor()
  393.  
  394.     class pack:
  395.         "stick stuff in this"
  396.     
  397.     def setup(self, pack=pack, editor=editor):
  398.         editor.cameraposdlg=self
  399.         self.pack=pack
  400.         cameras = filter(lambda d:d["macro"]=="cameraposition",editor.Root.findallsubitems("",":d"))
  401.         pack.cameras = cameras
  402.         pack.slist = map(lambda obj:obj.shortname, cameras)
  403.         pack.klist = map(lambda d:`d`, range(len(cameras)))
  404.         #
  405.         #  wtf doesn't this work, item loads but function is trashed
  406.         #
  407. #        self.src["cameras"] = pack.klist[0]
  408.         self.src["cameras$Items"] = "\015".join(pack.slist)
  409.         self.src["cameras$Values"] = "\015".join(pack.klist)
  410.         self.src["num"]=len(pack.klist),
  411.  
  412.     def action(self, pack=pack, editor=editor):
  413.        src = self.src
  414.        #
  415.        # note what's been chosen
  416.        #
  417.        self.chosen = src["cameras"]
  418.          
  419.     FindCameraPosDlg(quarkx.clickform, 'findcamerapos', editor, setup, action)
  420.  
  421. mapsearch.items.append(qmenu.item('Find Camera Positions', findClick,
  422.  "|Find Camera Positions:\n\nThis finds all the camera positions.|intro.mapeditor.menu.html#searchmenu"))
  423.  
  424.  
  425. #
  426. # Prev/Next hotkey subversion
  427. #
  428. def camnextClick(m, editor=None, oldnext=mapselection.nextClick):
  429.     if quarkx.keydown('C')==1:
  430.         editor=mapeditor()
  431.         if editor is None:
  432.            quarkx.msgbox('oops no editor',2,4)
  433.            return
  434.         try:
  435.             current=editor.currentcampos
  436.         except (AttributeError):
  437.             quarkx.msgbox("You need to set or store a view first for this to work",2,4)
  438.             return
  439.         successor = m.succ(current) # succ=prev or next, depending on key
  440.         #
  441.         # Skip over any non-camera stuff
  442.         #
  443.         while successor is not current and successor["macro"]!="cameraposition":
  444.             successor=m.succ(successor)
  445.         setView(successor,editor)
  446.     else:
  447.         oldnext(m,editor)
  448.  
  449. mapselection.nextItem.onclick=camnextClick
  450. mapselection.prevItem.onclick=camnextClick
  451.  
  452.  
  453.  
  454. # $Log: mapcamerapos.py,v $
  455. # Revision 1.10  2003/12/18 21:51:46  peter-b
  456. # Removed reliance on external string library from Python scripts (second try ;-)
  457. #
  458. # Revision 1.9  2003/03/21 05:47:45  cdunde
  459. # Update infobase and add links
  460. #
  461. # Revision 1.8  2002/05/21 21:30:21  tiglari
  462. # no-selection tree-view RMB bug fixed (ut_fourdays)
  463. #
  464. # Revision 1.7  2001/06/17 21:10:57  tiglari
  465. # fix button captions
  466. #
  467. # Revision 1.6  2001/06/17 04:46:11  tiglari
  468. # local camera positioning, and auto 3d view opening where appropriate
  469. #
  470. # Revision 1.5  2001/06/16 21:55:46  tiglari
  471. # dialog npw stays open when something else is selected,add position
  472. # on map background menu
  473. #
  474. # Revision 1.4  2001/06/16 03:19:05  tiglari
  475. # add Txt="" to separators that need it
  476. #
  477. # Revision 1.3  2001/06/16 02:44:09  tiglari
  478. # Camera Position finder dialog, cycle-in group with PgUp/Down+'C'
  479. #
  480. # Revision 1.2  2001/06/14 12:16:47  tiglari
  481. # nameing dialog, multiple view support (pick last clicked on)
  482. #
  483. # Revision 1.1  2001/06/13 22:28:09  tiglari
  484. # kickoff
  485. #
  486. #